package com.evan.review.thread;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.evan.review.R;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Activity demonstrating thread pool usage in Android
 */
public class ThreadPoolActivity extends AppCompatActivity {
    private static final String TAG = "ThreadPoolActivity";
    
    private TextView tvStatus;
    private RecyclerView recyclerView;
    private LogAdapter logAdapter;
    private final List<String> logs = new ArrayList<>();
    
    // Handler for updating UI from worker threads
    private final Handler mainHandler = new Handler(Looper.getMainLooper());
    
    // Different types of thread pools
    private ExecutorService fixedThreadPool;
    private ExecutorService cachedThreadPool;
    private ExecutorService singleThreadExecutor;
    private ScheduledExecutorService scheduledThreadPool;
    private ThreadPoolExecutor customThreadPool;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_thread_pool);
        
        initViews();
        initThreadPools();
        setupListeners();
    }
    
    private void initViews() {
        tvStatus = findViewById(R.id.tv_status);
        recyclerView = findViewById(R.id.recycler_view);
        
        // Setup recycler view with adapter
        logAdapter = new LogAdapter(logs);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(logAdapter);
    }
    
    private void initThreadPools() {
        // 1. Fixed Thread Pool
        fixedThreadPool = Executors.newFixedThreadPool(4);
        
        // 2. Cached Thread Pool
        cachedThreadPool = Executors.newCachedThreadPool();
        
        // 3. Single Thread Executor
        singleThreadExecutor = Executors.newSingleThreadExecutor();
        
        // 4. Scheduled Thread Pool
        scheduledThreadPool = Executors.newScheduledThreadPool(2);
        
        // 5. Custom Thread Pool
        // Create a thread factory that names threads and sets priority
        ThreadFactory threadFactory = new ThreadFactory() {
            private final AtomicInteger threadNumber = new AtomicInteger(1);
            
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r, "CustomPool-" + threadNumber.getAndIncrement());
                thread.setPriority(Thread.NORM_PRIORITY);
                return thread;
            }
        };
        
        // Create a custom rejection handler
        RejectedExecutionHandler rejectionHandler = (r, executor) -> {
            addLog("Task rejected: " + r.toString());
            
            // Fall back to caller thread execution
            if (!executor.isShutdown()) {
                r.run();
                addLog("Task executed in caller thread");
            }
        };
        
        // Create custom thread pool
        customThreadPool = new ThreadPoolExecutor(
                2,                        // Core pool size
                5,                        // Max pool size
                60L,                      // Keep alive time
                TimeUnit.SECONDS,         // Time unit
                new ArrayBlockingQueue<>(10), // Work queue with bounded capacity
                threadFactory,            // Thread factory
                rejectionHandler          // Rejection handler
        );
    }
    
    private void setupListeners() {
        // Fixed Thread Pool example
        Button btnFixed = findViewById(R.id.btn_fixed_pool);
        btnFixed.setOnClickListener(v -> {
            tvStatus.setText("固定线程池 (4个线程)");
            clearLogs();
            
            // Submit multiple tasks to fixed thread pool
            for (int i = 1; i <= 10; i++) {
                final int taskId = i;
                fixedThreadPool.submit(() -> executeTask("固定池-" + taskId, 500));
            }
        });
        
        // Cached Thread Pool example
        Button btnCached = findViewById(R.id.btn_cached_pool);
        btnCached.setOnClickListener(v -> {
            tvStatus.setText("缓存线程池 (动态大小)");
            clearLogs();
            
            // Submit many short tasks to demonstrate thread reuse
            for (int i = 1; i <= 20; i++) {
                final int taskId = i;
                cachedThreadPool.submit(() -> executeTask("缓存池-" + taskId, 300));
            }
        });
        
        // Single Thread Executor example
        Button btnSingle = findViewById(R.id.btn_single_pool);
        btnSingle.setOnClickListener(v -> {
            tvStatus.setText("单线程执行器");
            clearLogs();
            
            // Submit tasks that need sequential execution
            for (int i = 1; i <= 5; i++) {
                final int taskId = i;
                singleThreadExecutor.submit(() -> executeTask("单线程-" + taskId, 800));
            }
        });
        
        // Scheduled Thread Pool example
        Button btnScheduled = findViewById(R.id.btn_scheduled_pool);
        btnScheduled.setOnClickListener(v -> {
            tvStatus.setText("定时线程池");
            clearLogs();
            
            // Schedule a one-time task after delay
            scheduledThreadPool.schedule(
                    () -> executeTask("定时任务-延迟", 0),
                    2, TimeUnit.SECONDS
            );
            
            // Schedule a periodic task
            scheduledThreadPool.scheduleAtFixedRate(
                    () -> executeTask("定时任务-周期", 0),
                    1, 3, TimeUnit.SECONDS
            );
            
            addLog("任务已安排。周期性任务将每3秒执行一次");
        });
        
        // Custom Thread Pool example
        Button btnCustom = findViewById(R.id.btn_custom_pool);
        btnCustom.setOnClickListener(v -> {
            tvStatus.setText("自定义线程池 (2-5个线程, 队列=10)");
            clearLogs();
            
            // Show thread pool state before tasks
            logPoolState();
            
            // Submit many tasks to demonstrate queue and rejection
            for (int i = 1; i <= 20; i++) {
                final int taskId = i;
                try {
                    customThreadPool.submit(() -> executeTask("自定义-" + taskId, 1000));
                } catch (Exception e) {
                    addLog("异常: " + e.getMessage());
                }
            }
            
            // Show thread pool state after tasks
            mainHandler.postDelayed(this::logPoolState, 500);
        });
        
        // Future example with result
        Button btnFuture = findViewById(R.id.btn_future_result);
        btnFuture.setOnClickListener(v -> {
            tvStatus.setText("Future返回结果");
            clearLogs();
            
            // Submit callables that return results
            List<Future<Integer>> futures = new ArrayList<>();
            for (int i = 1; i <= 5; i++) {
                final int taskId = i;
                Future<Integer> future = fixedThreadPool.submit(new Callable<Integer>() {
                    @Override
                    public Integer call() throws Exception {
                        return processTaskWithResult("Future任务-" + taskId);
                    }
                });
                futures.add(future);
            }
            
            // Collect results in a separate thread to avoid blocking UI
            new Thread(() -> {
                try {
                    for (int i = 0; i < futures.size(); i++) {
                        Future<Integer> future = futures.get(i);
                        Integer result = future.get(); // This blocks until task completes
                        addLog("Future-" + (i + 1) + " 结果: " + result);
                    }
                    addLog("所有Future结果已收集");
                } catch (Exception e) {
                    addLog("获取Future结果错误: " + e.getMessage());
                }
            }).start();
        });
        
        // Shutdown all thread pools
        Button btnShutdown = findViewById(R.id.btn_shutdown);
        btnShutdown.setOnClickListener(v -> {
            tvStatus.setText("正在关闭所有线程池");
            clearLogs();
            
            // Demonstrate proper shutdown
            shutdownAndAwaitTermination(fixedThreadPool, "固定线程池");
            shutdownAndAwaitTermination(cachedThreadPool, "缓存线程池");
            shutdownAndAwaitTermination(singleThreadExecutor, "单线程执行器");
            shutdownAndAwaitTermination(scheduledThreadPool, "定时线程池");
            shutdownAndAwaitTermination(customThreadPool, "自定义线程池");
            
            // Reinitialize pools for further demonstration
            initThreadPools();
            addLog("线程池已重新初始化，可继续测试");
        });
    }
    
    // Execute a simple task for demonstration
    private void executeTask(String taskName, long duration) {
        long threadId = Thread.currentThread().getId();
        String threadName = Thread.currentThread().getName();
        
        addLog("Starting " + taskName + " on thread " + threadName + " (ID: " + threadId + ")");
        
        try {
            // Simulate work
            Thread.sleep(duration);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            addLog(taskName + " was interrupted");
            return;
        }
        
        addLog("Completed " + taskName);
    }
    
    // Process task that returns a result
    private Integer processTaskWithResult(String taskName) throws InterruptedException {
        long threadId = Thread.currentThread().getId();
        String threadName = Thread.currentThread().getName();
        
        addLog("Processing " + taskName + " on thread " + threadName + " (ID: " + threadId + ")");
        
        // Simulate work
        Thread.sleep(1000);
        
        // Generate a "result" - in a real app this would be meaningful data
        int result = (int)(Math.random() * 100);
        
        addLog(taskName + " produced result: " + result);
        return result;
    }
    
    // Log current state of custom thread pool
    private void logPoolState() {
        addLog("-------- Pool State --------");
        addLog("Active threads: " + customThreadPool.getActiveCount());
        addLog("Core pool size: " + customThreadPool.getCorePoolSize());
        addLog("Current pool size: " + customThreadPool.getPoolSize());
        addLog("Largest pool size: " + customThreadPool.getLargestPoolSize());
        addLog("Max pool size: " + customThreadPool.getMaximumPoolSize());
        addLog("Task count: " + customThreadPool.getTaskCount());
        addLog("Completed tasks: " + customThreadPool.getCompletedTaskCount());
        addLog("Queue size: " + customThreadPool.getQueue().size());
        addLog("----------------------------");
    }
    
    // Properly shutdown an ExecutorService
    private void shutdownAndAwaitTermination(ExecutorService pool, String poolName) {
        addLog("Shutting down " + poolName);
        pool.shutdown(); // Disable new tasks from being submitted
        
        try {
            // Wait for existing tasks to terminate
            if (!pool.awaitTermination(3, TimeUnit.SECONDS)) {
                // Cancel currently executing tasks forcefully
                addLog(poolName + " did not terminate in time, forcing shutdown");
                List<Runnable> droppedTasks = pool.shutdownNow();
                addLog(poolName + ": " + droppedTasks.size() + " tasks never commenced execution");
                
                // Wait a while for tasks to respond to being cancelled
                if (!pool.awaitTermination(3, TimeUnit.SECONDS)) {
                    addLog(poolName + " did not terminate");
                }
            } else {
                addLog(poolName + " shutdown complete");
            }
        } catch (InterruptedException e) {
            // (Re-)Cancel if current thread also interrupted
            pool.shutdownNow();
            // Preserve interrupt status
            Thread.currentThread().interrupt();
            addLog(poolName + " shutdown interrupted");
        }
    }
    
    // Add log message to UI
    private void addLog(String message) {
        Log.d(TAG, message);
        mainHandler.post(() -> {
            logs.add(message);
            logAdapter.notifyItemInserted(logs.size() - 1);
            recyclerView.smoothScrollToPosition(logs.size() - 1);
        });
    }
    
    // Clear logs
    private void clearLogs() {
        mainHandler.post(() -> {
            int size = logs.size();
            logs.clear();
            logAdapter.notifyItemRangeRemoved(0, size);
        });
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        
        // Properly shutdown all thread pools
        shutdownAndAwaitTermination(fixedThreadPool, "Fixed Pool");
        shutdownAndAwaitTermination(cachedThreadPool, "Cached Pool");
        shutdownAndAwaitTermination(singleThreadExecutor, "Single Thread Pool");
        shutdownAndAwaitTermination(scheduledThreadPool, "Scheduled Pool");
        shutdownAndAwaitTermination(customThreadPool, "Custom Pool");
    }
    
    /**
     * Adapter for displaying log messages
     */
    private static class LogAdapter extends RecyclerView.Adapter<LogAdapter.LogViewHolder> {
        private final List<String> logs;
        
        public LogAdapter(List<String> logs) {
            this.logs = logs;
        }
        
        @Override
        public LogViewHolder onCreateViewHolder(android.view.ViewGroup parent, int viewType) {
            android.view.View view = android.view.LayoutInflater.from(parent.getContext())
                    .inflate(android.R.layout.simple_list_item_1, parent, false);
            return new LogViewHolder(view);
        }
        
        @Override
        public void onBindViewHolder(LogViewHolder holder, int position) {
            holder.textView.setText(logs.get(position));
            // Alternate background colors for better readability
            if (position % 2 == 0) {
                holder.textView.setBackgroundColor(0xFFEEEEEE);
            } else {
                holder.textView.setBackgroundColor(0xFFFFFFFF);
            }
        }
        
        @Override
        public int getItemCount() {
            return logs.size();
        }
        
        static class LogViewHolder extends RecyclerView.ViewHolder {
            TextView textView;
            
            LogViewHolder(android.view.View itemView) {
                super(itemView);
                textView = (TextView) itemView.findViewById(android.R.id.text1);
                textView.setTextSize(12);
            }
        }
    }
} 